package main

import (

	"fmt"
	pd "github.com/hyperledger/fabric/protos/peer"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"strconv"
)

type ShunFengCoinChaincode struct {

}

// ********** chaincode begin ********** //
func (t *ShunFengCoinChaincode) Init(stub shim.ChaincodeStubInterface) pd.Response {
	return shim.Success(nil)
}

func (t *ShunFengCoinChaincode) Invoke(stub shim.ChaincodeStubInterface) pd.Response {

	fn, args := stub.GetFunctionAndParameters()
	fmt.Println("invoke is running " + fn)

	if fn == "name" {
		return t.getCoinFullName(stub, args)
	} else if fn == "symbol" {
		return t.getCoinSimpleName(stub, args)
	} else if fn == "decimals" {
		return t.decimals(stub, args)
	} else if fn == "totalSupply" {
		return t.totalSupply(stub, args)
	} else if fn == "balanceOf" {
		return t.balanceOf(stub, args)
	} else if fn == "transferFrom" {
		return t.transferFrom(stub, args)
	} else if fn == "approve" {
		return t.approve(stub, args)
	} else if fn == "allowance" {
		return t.allowance(stub, args)
	} else if fn == "new" {
		return t.createNewAccount(stub, args)
	} else if fn == "update" {
		return t.modifyAccountBalance(stub, args)
	}

	fmt.Println("invoke did not find func: " + fn) 
	return shim.Error("Received unknown function invocation")

}

//开户
func (t *ShunFengCoinChaincode) createNewAccount(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are opening an account ! ")
	if len(args) != 2 {
		return shim.Error("Incorrect number of arguments. Expecting 2 args : (accountName,initTokensAmount)")
	}
	accountName := "account_"+args[0]
	account, err := stub.GetState(accountName)
	if err != nil {
		return shim.Error("Failed to get user message:" + err.Error())
	} else if account != nil {
		return shim.Error("Account "+accountName+" already exist,please check the account ! ")
	}
	initTokensAmount := args[1]
	err = stub.PutState(accountName,[]byte(initTokensAmount))
	if err != nil {
		return shim.Error("Failed to opem account "+accountName)
	}
	return shim.Success([]byte("Congratulations ! Your account "+args[0]+" has been successfully opened ! "))
}

//修改账户余额
func (t *ShunFengCoinChaincode) modifyAccountBalance(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are modify the balance of someone's account ! ")
	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 2(accountName,flag,tokensAmount)")
	}
	accountName := "account_"+args[0]
	account, err := stub.GetState(accountName)
	if err != nil {
		return shim.Error("Failed to get account message:" + err.Error())
	} else if account == nil {
		return shim.Error("Account "+accountName+" does not exist,please check the account ! ")
	}
	accountBalance,_ := strconv.Atoi(string(account))
	flag := args[1]
	tokensAmount,_ := strconv.Atoi(args[2])
	if flag == "-" {
		accountBalance -= tokensAmount
	}else if flag == "+" {
		accountBalance += tokensAmount
	}else {
		return shim.Error("Failed to execute , because currently only supports '-' and '+', but requires to do "+flag+" ! ")
	}
	err = stub.PutState(accountName,[]byte(strconv.Itoa(accountBalance)))
	if err != nil {
		return shim.Error("Failed to execute , because "+ err.Error())
	}
	return shim.Success([]byte("Amazing ! Your operation was successfully executed ! "))
}

//获取代币全称
func (t *ShunFengCoinChaincode) getCoinFullName(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are querying the full name of this kind of token ! ")
	if len(args) != 0 {
		return shim.Error("Incorrect number of arguments. Expecting 0()")
	}
	return shim.Success([]byte("ShunFengCoin"))
}

//获取代币简称
func (t *ShunFengCoinChaincode) getCoinSimpleName(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are querying the abbreviation of this kind of token ! ")
	if len(args) != 0 {
		return shim.Error("Incorrect number of arguments. Expecting 0()")
	}
	return shim.Success([]byte("SFC"))
}

//获取代币所支持的精度
func (t *ShunFengCoinChaincode) decimals(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are querying the decimals of this kind of token ! ")
	if len(args) != 0 {
		return shim.Error("Incorrect number of arguments. Expecting 0()")
	}
	return shim.Success([]byte("5"))
}

//获取代币总量
func (t *ShunFengCoinChaincode) totalSupply(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are querying the amount of this kind of token ! ")
	if len(args) != 0 {
		return shim.Error("Incorrect number of arguments. Expecting 0()")
	}
	return shim.Success([]byte("100000000"))
}

//获取某账户余额
func (t *ShunFengCoinChaincode) balanceOf(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are querying the balance of someone's account ! ")
	if len(args) != 1 {
		return shim.Error("Incorrect number of arguments. Expecting 1(userAccount)")
	}
	userAccount := "account_"+args[0]
	accountMessage, err := stub.GetState(userAccount)
	if err != nil {
		return shim.Error("Failed to get user balance:" + err.Error())
	} else if accountMessage == nil {
		return shim.Error("Account does not exist")
	}
	fmt.Printf("Query Response:%s\n", string(accountMessage))
	return shim.Success(accountMessage)
}

//转账操作
func (t *ShunFengCoinChaincode) transferFrom(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are transfering ! ")
	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 3(fromWho,toWho,tokenAmount)")
	}
	fromWho := "account_"+args[0]
	toWho := "account_"+args[1]
	tokenAmount,_ := strconv.Atoi(args[2])
	fromAccount, err0 := stub.GetState(fromWho)
	if err0 != nil {
		return shim.Error("Failed to get user message:" + err0.Error())
	} else if fromAccount == nil {
		return shim.Error("Account"+fromWho+" does not exist")
	}
	fromWhoBalance,_ :=strconv.Atoi(string(fromAccount))
	if fromWhoBalance < tokenAmount {
		return shim.Error("Failed to execute,because "+fromWho+" does not have enough tokens")
	}
	toAccount, err1 := stub.GetState(toWho)
	if err1 != nil {
		return shim.Error("Failed to get user balance:" + err1.Error())
	} else if toAccount == nil {
		return shim.Error("Account"+toWho+" does not exist")
	}
	toWhoBalance,_ :=strconv.Atoi(string(toAccount))

	fromWhoBalance =fromWhoBalance-tokenAmount
	toWhoBalance = toWhoBalance+tokenAmount

	err0 = stub.PutState(fromWho,[]byte(strconv.Itoa(fromWhoBalance)))
	if err0 != nil {
		return shim.Error("Failed to execute account "+fromWho)
	}
	err1 = stub.PutState(toWho,[]byte(strconv.Itoa(toWhoBalance)))
	if err1 != nil {
		return t.rollback(stub,args)
	}
	return shim.Success([]byte("success"))
}

//转账时发生错误的回滚操作
func (t *ShunFengCoinChaincode) rollback(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("In order to maintain system stability, a rollback operation is being performed ! ")
	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 3")
	}
	userAccount := "account_"+args[0]
	accountMessage, err := stub.GetState(userAccount)
	if err != nil {
		return shim.Error("Failed to get user balance:" + err.Error())
	} else if accountMessage == nil {
		return shim.Error("Account does not exist")
	}
	tokenAmount,_ :=strconv.Atoi(args[2])
	accountBalance,_ :=strconv.Atoi(string(accountMessage))
	accountBalance -=tokenAmount
	err = stub.PutState(userAccount,[]byte(strconv.Itoa(accountBalance)))
	if err != nil {
		return t.rollback(stub,args)
	}
	return shim.Success([]byte("success"))
}

//“开支票”
func (t *ShunFengCoinChaincode) approve(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are writting a 'check' ! ")
	if len(args) != 3 {
		return shim.Error("Incorrect number of arguments. Expecting 3(approveFrom,approveTo,tokenAmount)")
	}
	approveKey := "approve_"+args[0]+"_"+args[1]
	approveTokenAmount := args[2]
	err := stub.PutState(approveKey,[]byte(approveTokenAmount))
	if err != nil {
		return shim.Error("Failed to approve :" + err.Error())
	}
	return shim.Success([]byte("success"))
}

//获取“支票”中剩余可支配额度
func (t *ShunFengCoinChaincode) allowance(stub shim.ChaincodeStubInterface, args []string) pd.Response {
	fmt.Println("You are querying the remaining-amount of the 'check' ! ")
	if len(args) != 2 {
		return shim.Error("Incorrect number of arguments. Expecting 2(approveFrom，approveTo)")
	}
	approveKey := "approve_"+args[0]+"_"+args[1]
	approveBalance, err := stub.GetState(approveKey)
	if err != nil {
		return shim.Error("Failed to get 'check' balance:" + err.Error())
	} else if approveBalance == nil {
		return shim.Error("approve does not exist")
	}
	return shim.Success(approveBalance)
}

// ********** main ********** //
func main() {
	if err := shim.Start(new(ShunFengCoinChaincode)); err != nil {
		fmt.Printf("Error starting student chaincode: %s", err)
	}
}